package com.wyd.common.util;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Supplier;

/**
 * json工具 使用jackson实现
 */
@Slf4j
public class JsonUtils {
    private JsonUtils() {
    }

    public static final ObjectMapper objectMapper;

    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    /*
      设置一些通用的属性
     */
    static {
        // 时间等格式
        final JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

        // Date时间 反序列化
        javaTimeModule.addDeserializer(Date.class, new DateDeserializers());

        objectMapper = JsonMapper
                .builder()
                // 如果存在未知属性，则忽略不报错
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                // // 允许整数以0开头
                .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS)
                // 允许key没有双引号
                .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
                // // 允许key有单引号
                .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES)
                // 允许字符串中存在回车换行控制符
                .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
                .addModule(new Jdk8Module())
                .addModule(javaTimeModule)
                // 处理特殊的转换
                .addHandler(new DeserializationProblemHandlerCustom())
                .build();
    }

    public static String toJSONString(Object obj) {
        return toJSONString(obj, () -> null, false);
    }

    public static String toFormatJSONString(Object obj) {
        return toJSONString(obj, () -> null, true);
    }

    public static String toJSONString(Object obj, Supplier<String> defaultSupplier, boolean format) {
        try {
            if (obj == null) {
                return defaultSupplier.get();
            }
            if (format) {
                return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            }
            return objectMapper.writeValueAsString(obj);
        } catch (Throwable e) {
            log.error("toJSONString {}", obj != null ? obj.toString() : "null", e);
        }
        return defaultSupplier.get();
    }

    public static <T> T parseObject(String value, Class<T> tClass) {
        return parseObject(value, tClass, () -> null);
    }

    /**
     * 解析泛型类型通用
     */
    public static <T> T parse(String value, TypeReference<T> valueTypeRef) {
        return parse(value, valueTypeRef, () -> null);
    }

    /**
     * 解析泛型类型通用
     */
    public static <T> T parse(String value, TypeReference<T> valueTypeRef, Supplier<T> defaultSupplier) {
        if (org.apache.commons.lang3.StringUtils.isBlank(value)) {
            return defaultSupplier.get();
        }

        try {
            return objectMapper.readValue(value, valueTypeRef);
        } catch (JsonProcessingException e) {
            log.error("objectMapper.readValue 出错 value={} valueTypeRef={}", value, valueTypeRef.getType(), e);
            return defaultSupplier.get();
        }
    }

    public static <T> T parseObject(Object obj, Class<T> tClass) {
        return parseObject(toJSONString(obj), tClass, () -> null);
    }

    public static <T> T parseObject(String value, Class<T> tClass, Supplier<T> defaultSupplier) {
        try {
            if (StringUtils.isBlank(value)) {
                return defaultSupplier.get();
            }
            return objectMapper.readValue(value, tClass);
        } catch (Throwable e) {
            log.error("parseObject exception: \n {}\n {}", value, tClass, e);
        }
        return defaultSupplier.get();
    }

    public static <T> List<T> parseArray(String value, Class<T> tClass) {
        return parseArray(value, tClass, () -> null);
    }

    public static <T> List<T> parseArray(Object obj, Class<T> tClass) {
        return parseArray(toJSONString(obj), tClass, () -> null);
    }

    public static <T> List<T> parseArray(String value, Class<T> tClass, Supplier<List<T>> defaultSupplier) {
        try {
            if (StringUtils.isBlank(value)) {
                return defaultSupplier.get();
            }
            JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, tClass);
            return objectMapper.readValue(value, javaType);
        } catch (Throwable e) {
            log.error("parseArray exception \n{}\n{}", value, tClass, e);
        }
        return defaultSupplier.get();
    }

    /**
     * 解析json为JsonNode
     */
    public static JsonNode parseObject(String value) {
        if (StringUtils.isBlank(value)) {
            return null;
        }

        try {
            return objectMapper.readTree(value);
        } catch (JsonProcessingException e) {
            log.error("解析json为JsonNode错误", e);
            return null;
        }
    }

    /**
     * 序列化为字节数组
     */
    public static byte[] toJSONBytes(Object obj, String defaultValue) {
        try {
            return objectMapper.writeValueAsBytes(obj);
        } catch (JsonProcessingException e) {
            log.error("objectMapper.writeValueAsBytes出错", e);
            return defaultValue.getBytes(StandardCharsets.UTF_8);
        }
    }

    // 简单地直接用json复制或者转换(Cloneable)
    public static <T> T jsonCopy(Object obj, Class<T> tClass) {
        return obj != null ? parseObject(toJSONString(obj), tClass) : null;
    }

    /**
     * json解析为Map
     */
    public static Map<String, Object> toMap(String value) {
        return toMap(value, () -> null);
    }

    /**
     * json解析为List<Map>
     */
    public static List<Map<String, Object>> toMapList(String value) {
        return toMapList(value, () -> null);
    }

    /**
     * 数组型 json解析为List<String>
     */
    public static List<String> toListString(String value) {
        return parse(value, new TypeReference<List<String>>() {
        });
    }

    /**
     * json解析为List<Map>
     */
    public static List<Map<String, Object>> toMapList(String value, Supplier<List<Map<String, Object>>> defaultSupplier) {
        return parse(value, new TypeReference<List<Map<String, Object>>>() {
        }, defaultSupplier);
    }

    public static Map<String, Object> toMap(Object value) {
        return toMap(value, () -> null);
    }

    public static Map<String, Object> toMap(Object value, Supplier<Map<String, Object>> defaultSupplier) {
        if (value == null) {
            return defaultSupplier.get();
        }
        try {
            if (value instanceof Map) {
                return (Map<String, Object>) value;
            }
        } catch (Exception e) {
            log.info("fail to convert" + toJSONString(value), e);
        }
        return toMap(toJSONString(value), defaultSupplier);
    }

    /**
     * json解析为Map
     */
    public static Map<String, Object> toMap(String value, Supplier<Map<String, Object>> defaultSupplier) {
        if (StringUtils.isBlank(value)) {
            return defaultSupplier.get();
        }
        try {
            return parse(value, new TypeReference<Map<String, Object>>() {
            });
        } catch (Exception e) {
            log.error("toMap exception\n{}", value, e);
        }
        return defaultSupplier.get();
    }


    public static List<Object> toList(String value) {
        return toList(value, () -> null);
    }

    public static List<Object> toList(Object value) {
        return toList(value, () -> null);
    }

    public static List<Object> toList(String value, Supplier<List<Object>> defaultSuppler) {
        if (StringUtils.isBlank(value)) {
            return defaultSuppler.get();
        }
        try {
            return parseObject(value, List.class);
        } catch (Exception e) {
            log.error("toList exception\n" + value, e);
        }
        return defaultSuppler.get();
    }

    public static List<Object> toList(Object value, Supplier<List<Object>> defaultSuppler) {
        if (value == null) {
            return defaultSuppler.get();
        }
        if (value instanceof List) {
            return (List<Object>) value;
        }
        return toList(toJSONString(value), defaultSuppler);
    }

    public static long getLong(Map<String, Object> map, String key) {
        if (MapUtils.isEmpty(map)) {
            return 0L;
        }
        String valueStr = String.valueOf(map.get(key));
        if (StringUtils.isBlank(valueStr) || !StringUtils.isNumeric(valueStr)) {
            return 0L;
        }
        return Long.valueOf(valueStr);
    }

    public static int getInt(Map<String, Object> map, String key) {
        if (MapUtils.isEmpty(map)) {
            return 0;
        }
        String valueStr = String.valueOf(map.get(key));
        if (StringUtils.isBlank(valueStr) || !StringUtils.isNumeric(valueStr)) {
            return 0;
        }
        return Integer.valueOf(valueStr);
    }

    /**
     * 解析json时间
     */
    public static class DateDeserializers extends com.fasterxml.jackson.databind.deser.std.DateDeserializers.DateDeserializer {
        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            try {
                // 兼容一些通用的
                return super.deserialize(p, ctxt);
            } catch (InvalidFormatException e) {
                final String text = p.getText().trim();

                try {
                    return DateUtils.parseDate(text, parsePatterns);
                } catch (ParseException parseException) {
                    log.error("json解析时间报错", parseException);
                    return null;
                }
            }
        }
    }

    /**
     * 特殊的json字符串转String
     */
    public static class DeserializationProblemHandlerCustom extends DeserializationProblemHandler {
        @Override
        public Object handleUnexpectedToken(DeserializationContext ctxt,
                                            JavaType targetType, JsonToken t, JsonParser p,
                                            String failureMsg) throws IOException {
            if (targetType.hasRawClass(String.class)) {
                // String类型
                if (t == JsonToken.START_OBJECT) {
                    return ctxt.readTree(p).toString();
                } else if (t == JsonToken.START_ARRAY) {
                    return ctxt.readTree(p).toString();
                }
            }

            log.error("未知的处理 targetType={} JsonToken={}", targetType, t);

            return NOT_HANDLED;
        }
    }
}
